﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Rappelz.GameServer
{
    public class Roamer : GameObject, IMonsterDeleteHandler
    {
        public class PendingHateShareInfo
        {
            // Function       :   public void PENDING_HATE_SHARE_INFO(const unsigned int, const unsigned int, const int)
            public uint m_hRequester;           // 0x0
            public uint m_hHateTarget;          // 0x4
            public int m_nHate;                 // 0x8

        }

        public class RoamingCreatureInfo : GameContent.RoamingCreatureRespawnInfo
        {
            // Function       :   public void ROAMING_CREATURE_INFO(const struct GameContent::ROAMING_CREATURE_RESPAWN_INFO &)
            // Function       :   public void ROAMING_CREATURE_INFO(const enum StructRoamer::ROAMING_CREATURE_TYPE, const int, const unsigned long, const int, const float)
            public Creature m_pCreature;        // 0x14
            public uint m_nNextRespawnTime;     // 0x18

        }


        public enum RoamingStatus : int
        {
            Idle = 0,
            Roaming = 1,
            Rotating = 2,
            Paused = 3,
        }

        public enum RoamingType : int
        {
            Round = 0,
            GoBack = 1,
        }
        
        public enum HateType : int
        {
            Individual = 0,
            ShareFirst = 1,
            FullShare = 4,
        }

        public enum CreatureType : int
        {
            Monster = 0,
            NPC = 1,
        }

        public enum Direction : int
        {
            Forward = 0,
            Backward = 1,
        }

// UserDefinedType: StructRoamer
// BaseClass      :   ArObject, offset = 0x0
// BaseClass      :     ArSchedulerObject, offset = 0x0
// 
// Enum           :     ObjectType, Type: int
// Function       :     public enum ArObject::ObjectType GetObjectType()
// Data           :     this+0x40, Member, Type: int, region_index
// Function       :     public void ArObject(const struct ArObject &)
// Function       :     public void ArObject::ArObject(enum ArObject::ObjectType)
// Function       :     public float GetX()
// Function       :     public float GetY()
// Function       :     public float GetZ()
// Function       :     public unsigned char GetLayer()
// Function       :     public void ArObject::AddNoise(int, int, int)
// Function       :     public const struct ArPosition & GetPos()
// Function       :     public const struct ArMoveVector & GetMv()
// Function       :     public unsigned int GetRX()
// Function       :     public unsigned int GetRY()
// Function       :     public struct ArPosition ArObject::GetCurrentPosition(unsigned long)
// Function       :     public void SetTag(void *)
// Function       :     public void * GetTag()
// Function       :     public bool IsMoving(unsigned long)
// Function       :     public bool IsMoving()
// Function       :     public bool IsInWorld()
// Function       :     public bool ArObject::Step(unsigned long)
// Function       :     public void SetMove(float, float, unsigned char, unsigned long)
// Function       :     public void ArObject::SetMove(const struct ArPosition &, unsigned char, unsigned long)
// Function       :     public void ArObject::SetMultipleMove(const class std::vector<ArPosition,std::allocator<ArPosition> > &, unsigned char, unsigned long)
// Function       :     public void ArObject::StopMove()
// Function       :     public float GetFace()
// Function       :     public void SetFace(float)
// Function       :     public float GetTX()
// Function       :     public float GetTY()
// Function       :     public float GetTZ()
// Function       :     public const class std::vector<ArMoveVector::MOVE_INFO,std::allocator<ArMoveVector::MOVE_INFO> > & GetTargetPosList()
// Function       :     public const struct ArPosition & GetTargetPos()
// Function       :     public unsigned char GetSpeed()
// Function       :     public unsigned long GetStartTime()
// Function       :     public void SetDirection(const struct ArPosition &)
// Function       :     public bool IsLookingRegion(unsigned int, unsigned int)
// Function       :     public void ArObject::SetCurrentXY(float, float)
// Function       :     public void SetCurrentZ(float)
// Function       :     public void SetCurrentLayer(unsigned char)
// Data           :     this+0x44, Member, Type: volatile bool, bIsInWorld
// Data           :     this+0x45, Member, Type: volatile bool, bIsNearClient
// Data           :     this+0x48, Member, Type: int, prev_rx
// Data           :     this+0x4C, Member, Type: int, prev_ry
// Data           :     this+0x50, Member, Type: bool, bIsRegionChanging
// Data           :     this+0x54, Member, Type: unsigned long, lastStepTime
// Data           :     this+0x58, Member, Type: struct ArRegion *, pRegion
// Data           :     this+0x5C, Member, Type: unsigned int, nListIndex
// Data           :     this+0x60, Member, Type: unsigned int, nActiveIndex
// Function       :     protected void ArObject::~ArObject()
// Data           :     this+0x64, Member, Type: unsigned char, m_nArObjectType
// Data           :     this+0x68, Member, Type: void *, pTag
// Data           :     this+0x6C, Member, Type: struct ArMoveVector, mv
// 
// Data           :     this+0xAC, Member, Type: unsigned char, layer
// Data           :     this+0xAD, Member, Type: bool, bIsDeleted
// Function       :     public struct ArObject & operator=(const struct ArObject &)
// Function       :     public void __dflt_ctor_closure()
// Function       :     public void __local_vftable_ctor_closure()
// Function       :     protected void * __vecDelDtor(unsigned int)
// 
// BaseClass      :   StructMonster::MonsterDeleteHandler, offset = 0xB0
// VTable         :     , Type:  *
// Function       :     public void onMonsterDelete(struct StructMonster *)
// Function       :     public void MonsterDeleteHandler(const struct StructMonster::MonsterDeleteHandler &)
// Function       :     public void MonsterDeleteHandler()
// Function       :     public struct StructMonster::MonsterDeleteHandler & operator=(const struct StructMonster::MonsterDeleteHandler &)
// 
// BaseClass      :   StructNPC::NPCDeadHandler, offset = 0xB4
// VTable         :     , Type:  *
// Function       :     public void onNPCDead(struct StructNPC *)
// Function       :     public void NPCDeadHandler(const struct StructNPC::NPCDeadHandler &)
// Function       :     public void NPCDeadHandler()
// Function       :     public struct StructNPC::NPCDeadHandler & operator=(const struct StructNPC::NPCDeadHandler &)
// 
// UserDefinedType:   ROAMING_CREATURE_INFO
// BaseClass      :     GameContent::ROAMING_CREATURE_RESPAWN_INFO, offset = 0x0
// 
// Function       :     public void ROAMING_CREATURE_INFO(const struct GameContent::ROAMING_CREATURE_RESPAWN_INFO &)
// Function       :     public void ROAMING_CREATURE_INFO(const enum StructRoamer::ROAMING_CREATURE_TYPE, const int, const unsigned long, const int, const float)
// Data           :     this+0x14, Member, Type: struct StructCreature *, m_pCreature
// Data           :     this+0x18, Member, Type: unsigned long, m_nNextRespawnTime
// UserDefinedType:   PENDING_HATE_SHARE_INFO
// Function       :     public void PENDING_HATE_SHARE_INFO(const unsigned int, const unsigned int, const int)
// Data           :     this+0x0, Member, Type: unsigned int, m_hRequester
// Data           :     this+0x4, Member, Type: unsigned int, m_hHateTarget
// Data           :     this+0x8, Member, Type: int, m_nHate
// Function       :   public void StructRoamer(const struct StructRoamer &)
// Function       :   public void StructRoamer::StructRoamer(const int, const enum StructRoamer::ROAMING_TYPE, const int, const enum StructRoamer::HATE_TYPE, const unsigned long, const int, const bool)

        public Roamer(int nID, RoamingType eRoamingType, int nMoveSpeed, HateType eHateType, uint nRespawnInterval, int nAttributeFlag, bool bIsRaidDungeonRoamer)
        {
            this.m_nArObjectType = 1;
//             v9->baseclass_b0.vfptr = &StructMonster::MonsterDeleteHandler::_vftable_;
//             v9->baseclass_b4.vfptr = &StructNPC::NPCDeadHandler::_vftable_;
//             v9->baseclass_b0.vfptr = &StructRoamer::_vftable_;
//            v9->baseclass_b4.vfptr = &StructRoamer::_vftable_;
//            v9->baseclass_0.baseclass_0.vfptr = &StructRoamer::_vftable_;

            this.m_nID = nID;
            this.m_nMoveSpeed = nMoveSpeed;
            this.m_eRoamingType = eRoamingType;
            this.m_eHateType = eHateType;
            this.m_nRespawnInterval = nRespawnInterval;
            this.m_AttributeFlag = nAttributeFlag;
            this.m_eCurrentRoamingDirection = Direction.Forward;
            this.m_nCurrentRoamingPointIndex = 0;
            this.m_eRoamingStatus = 0;
            this.m_bIsRaidDungeonRoamer = bIsRaidDungeonRoamer;
            this.m_nNextRespawnProcTime = 0;
        }

// Function       :   public void StructRoamer::~StructRoamer()
// Function       :   public bool StructRoamer::ProcDelete()
// Function       :   public bool StructRoamer::IsRoamer()
// Function       :   public const int GetRoamingID()

        public void AddCreatureRespawnInfo(GameContent.RoamingCreatureRespawnInfo info)
        {
            lock(this)
            {
                RoamingCreatureInfo ni = new RoamingCreatureInfo();
                ni.m_pCreature = null;
                ni.m_nNextRespawnTime = 0;
                ni.m_eCreatureType = info.m_eCreatureType;
                ni.m_nAngle = info.m_nAngle;
                ni.m_nCreatureID = info.m_nCreatureID;
                ni.m_nDistance = info.m_nDistance;
                ni.m_nRespawnInterval = info.m_nRespawnInterval;
                this.m_vRoamingCreatureRespawnInfo.Add(ni);
            }
        }
// Function       :   public const unsigned int GetCreatureRespawnInfoCount()

        public void AddRoamingPoint(ArPosition pos)
        {
            lock(this)
            {
                if (this.priority != Priority.High)
                {
                    if (!this.bIsInWorld )
                    {
                        this.m_vRoamingPoint.Add(pos);
                        int idx = this.m_vRoamingPoint.Count - 1;
                        if (idx != 0)
                        {
                            if (idx == 1)
                                this.mv.SetDirection(pos);
                        }
                        else
                        {
                            this.SetCurrentXY(pos.x,pos.y);
                        }
                    }
                }
            }
        }
// Function       :   public const unsigned int GetRoamingPointCount()

        public void DeleteRespawnedCreature()
        {
            Creature pCreature;

            lock(this)
            {
                this.m_nNextRespawnProcTime = 0;
                foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
                {
                    pCreature = rci.m_pCreature;
                    if (pCreature != null)
                    {
                        rci.m_nNextRespawnTime = 0;
                        rci.m_pCreature = null;
                        if (pCreature.bIsInWorld)
                        {
                            RLock rl = RappelzServer.Instance._LockObjectWithVisibleRange(pCreature);
                            AutoLock al = new AutoLock(rl.handle);

                            if (pCreature.m_bIsEnable)
                            {
                                pCreature.m_bIsEnable = false;
                                if (rci.m_eCreatureType == 1)
                                {
                                    if (pCreature.bIsInWorld)
                                        GameContent.RemoveNPCFromWorld(pCreature as NPC);
                                    //(pCreature as NPC).m_pDeadHandler = null;
                                }
                                else
                                {
                                    if (pCreature.bIsInWorld)
                                        GameContent.RemoveMonsterFromWorld(pCreature as Monster);
                                    (pCreature as Monster).m_pDeleteHandler = null;
                                }
                                RappelzServer.Instance.DeleteObject(pCreature);
                            }
//                            ArcadiaAutoLock::_ArcadiaAutoLock((v1 - 56));
                        }
                    }
                }
            }

        }

        public bool Init()
        {
            if (this.priority == Priority.High)
                return false;

            this.m_nCurrentRoamingPointIndex = 0;
            this.m_eCurrentRoamingDirection = Direction.Forward;
            ArPosition firstPos = this.m_vRoamingPoint.First();
            this.SetCurrentXY(firstPos.x,firstPos.y);
            this.m_eRoamingStatus = RoamingStatus.Roaming;
            if (this.m_nRespawnInterval == 0)
            {
                lock(this) {
                    foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
                    {
                        rci.m_nNextRespawnTime = Globals.GetArTime();
                    }
                }
            }
            else
            {
                this.m_nNextRespawnProcTime = Globals.GetArTime();
            }
            RLock rl = RappelzServer.Instance._LockObjectWithVisibleRange(this);
            AutoLock al = new AutoLock(rl.handle);
            RappelzServer.Instance.AddObject(this);
//            ArcadiaAutoLock::_ArcadiaAutoLock((v1 - 60));

            RappelzServer.Instance.SetObjectPriority(this, Priority.High);
            return true;

        }

        public bool DeInit(bool bForceToDeleteEverlastingRoamer)
        {
            if (this.priority == SchedulerObject.Priority.High)
            {
                RappelzServer.Instance.SetObjectPriority(this, Priority.Idle);
                this.m_vPendingHateInfo.Clear();
                this.DeleteRespawnedCreature();
                if (this.bIsInWorld)
                {
                    RLock rl = RappelzServer.Instance._LockObjectWithVisibleRange(this);
                    AutoLock al = new AutoLock(rl.handle);
                    RappelzServer.Instance.RemoveObject(this);
                    //                    ArcadiaAutoLock::_ArcadiaAutoLock((v2 - 60));
                }
                if (!this.m_bIsRaidDungeonRoamer || bForceToDeleteEverlastingRoamer)
                {
                    RoamingManager.Instance.UnregisterRoamingInfo(this.m_nID);
                    RappelzServer.Instance.DeleteObject(this);
                }
                return true;
            }
            return false;
        }

// Function       :   public const bool IsInitialized()

        public override void onProcess(int nThreadIdx)
        {
            ArPosition currentPos;          // 24   18h
            ArPosition pos;                 // 44   2Ch
            float fFace;                    // 96   60h
            uint t;                         // 100  64h

            lock(this)
            {
                if (this.priority != Priority.High)
                    RappelzServer.Instance.SetObjectPriority(this, Priority.High);

                t = Globals.GetArTime();

                RLock rl = RappelzServer.Instance._LockObject(this);
                AutoLock al = new AutoLock(rl.handle);

                currentPos = this.GetCurrentPosition(t);
                fFace = this.mv.face;
                //ArcadiaAutoLock::_ArcadiaAutoLock((v2 - 68));
                if (this.m_nRespawnInterval == 0
                  || this.m_eRoamingStatus == RoamingStatus.Roaming && this.m_nNextRespawnProcTime != 0 && this.m_nNextRespawnProcTime <= t)
                {
                    if(this.m_vRoamingCreatureRespawnInfo.Count > 0)
                    {
                        foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
                        {
    //             public int m_eCreatureType;                 // 0x0
    //             public int m_nCreatureID;                   // 0x4
    //             public uint m_nRespawnInterval;             // 0x8
    //             public int m_nAngle;                        // 0xC
    //             public float m_nDistance;                   // 0x10

    //             public Creature m_pCreature;        // 0x14
    //             public uint m_nNextRespawnTime;     // 0x18

                            if (rci.m_pCreature != null 
                                || this.m_nRespawnInterval == 0 
                                && (rci.m_nNextRespawnTime == 0 || rci.m_nNextRespawnTime > t))
                                continue;

                            if (rci.m_eCreatureType == 0)
                            {
                                Monster mob = Monster.AllocMonster((uint)rci.m_nCreatureID);

                                if (mob != null)
                                {
                                    rci.m_pCreature = mob;
                                    mob.m_nGenerateCode = 1;
                                    pos = Roamer.getCurrentRespawnObjectPosition(currentPos, ref fFace, ref rci.m_nAngle, ref rci.m_nDistance);
                                    mob.SetCurrentXY(pos.x, pos.y);
                                    mob.m_layer = this.m_layer;
                                    mob.m_pDeleteHandler = this;
                                    mob.m_bIsWandering = false;
                                    mob.m_pRoamer = this;

                                    rl = RappelzServer.Instance._LockObjectWithVisibleRange(mob);
                                    al = new AutoLock(rl.handle);
                                    GameContent.AddMonsterToWorld(mob);
                                    if (this.mv.bIsMoving)
                                    {
                                        if (this.bIsInWorld )
                                        {
                                            pos = Roamer.getCurrentRespawnObjectPosition(this.GetCurrentRoamingTargetPosition(), ref fFace, ref rci.m_nAngle, ref rci.m_nDistance);
                                            RappelzServer.Instance.SetMove(mob, currentPos, pos, (sbyte)this.m_nMoveSpeed, false, 0, true);
                                        }
                                    }
//                                    ArcadiaAutoLock::_ArcadiaAutoLock(v35);
                                }
                            }
                            else if (rci.m_eCreatureType == 1 ) // NPC's go here
                            {
                                NPCBase npcBase = GameContent.GetNPCInfo(rci.m_nCreatureID);

                                if (npcBase != null && npcBase.id != 0)
                                {
                                    // TODO - Add Periodic support here
                                    NPC newNPC = new NPC(npcBase);
                                    rci.m_pCreature = newNPC;
                                    pos = Roamer.getCurrentRespawnObjectPosition(currentPos, ref fFace, ref rci.m_nAngle, ref rci.m_nDistance);
                                    newNPC.SetCurrentXY(pos.x, pos.y);
                                    newNPC.m_layer = this.m_layer;
                                    //newNPC m_pDeadHandler = this;
                                    newNPC.m_pRoamer = this;

                                    rl = RappelzServer.Instance._LockObjectWithVisibleRange(newNPC);
                                    al = new AutoLock(rl.handle);
                                    GameContent.AddNPCToWorld(newNPC);
                                    if (this.mv.bIsMoving )
                                    {
                                        if (this.bIsInWorld )
                                        {
                                            pos = Roamer.getCurrentRespawnObjectPosition(this.GetCurrentRoamingTargetPosition(), ref fFace, ref rci.m_nAngle, ref rci.m_nDistance);
                                            RappelzServer.Instance.SetMove(newNPC, currentPos, pos, (sbyte)this.m_nMoveSpeed, false, 0, true);
                                        }
                                    }
//                                    ArcadiaAutoLock::_ArcadiaAutoLock(v35);
                                }
                            }
                        }
                    }
                    else
                    {
                        this.m_nNextRespawnProcTime = 0;
                    }
                }

                if (this.mv.bIsMoving && this.bIsInWorld )
                    this.processWalk(t);
                else
                   this.processRoaming(t, ref currentPos, ref fFace);
            }


            this.processHateSharing();

        }

        public void onMonsterDelete(Monster pMonster)
        {
            bool bNeedToDeleteRoamer = false;

            lock(this)
            {
                for(int i = this.m_vRoamingCreatureRespawnInfo.Count - 1; i>= 0; --i)
                {
                    RoamingCreatureInfo rci = this.m_vRoamingCreatureRespawnInfo[i];
                    if (rci.m_pCreature != null && rci.m_pCreature.IsSameObject(pMonster))
                    {
                        rci.m_pCreature = null;
                        if (this.m_bIsRaidDungeonRoamer)
                        {
                            rci.m_nNextRespawnTime = 0;
                        }
                        else if (this.m_nRespawnInterval != 0 || rci.m_nRespawnInterval == 0)
                        {
                            rci.m_nNextRespawnTime = 0;
                        }
                        else
                        {
                            rci.m_nNextRespawnTime = rci.m_nRespawnInterval + Globals.GetArTime();
                        }

                        if (this.m_nRespawnInterval == 0)
                        {
                            if (rci.m_nNextRespawnTime == 0)
                            {
                                this.m_vRoamingCreatureRespawnInfo.RemoveAt(i);
                                if (this.m_vRoamingCreatureRespawnInfo.Count == 0)
                                    bNeedToDeleteRoamer = true;
                            }
                        }
                        break;
                    }
                }

            }
            if (bNeedToDeleteRoamer)
               this.DeInit(false);
        }
// Function       :   public void StructRoamer::onNPCDead(struct StructNPC *)

        public override bool IsMovable()
        {
            bool res;
            lock(this)
            {
                res = false;
                if (this.m_eRoamingStatus != RoamingStatus.Idle)
                    res = this.isMovable();
            }
            return res;
        }
// Function       :   public const bool IsRaidDungeonRoamer()
// Function       :   public const bool IsPaused()

        public void PauseRoaming()
        {
// .text:00466428 _lock           = ArcadiaAutoLock ptr -60h
            ArPosition pos;             // -76  -4Ch
            ArPosition returnPos;       // -60  -3Ch
            float fFace;                // -16  -10h

            lock(this)
            {
                if (this.m_eRoamingStatus != RoamingStatus.Paused)
                {
                    pos = this.GetCurrentPosition(Globals.GetArTime());
                    fFace = this.mv.face;
                    this.m_eRoamingStatus = RoamingStatus.Paused;
                    this.StopMove();
                    RappelzServer.Instance.SetMove(this, pos,pos, 0,false,0,false);
                    foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
                    {
                        Creature cr = rci.m_pCreature;
                        if (cr != null)
                        {
                            if (cr.bIsInWorld)
                            {
                                returnPos = Roamer.getCurrentRespawnObjectPosition(pos, ref fFace, ref rci.m_nAngle, ref rci.m_nDistance);
                                if (cr.IsMonster())
                                {
                                    Monster mob = cr as Monster;
                                    mob.m_RespawnX = returnPos.x;
                                    mob.m_RespawnY = returnPos.y;
                                }
                                else if (cr.IsNPC())
                                {
                                    NPC n = cr as NPC;
                                    n.m_RespawnX = returnPos.x;
                                    n.m_RespawnY = returnPos.y;
                                }

                                RLock rl = RappelzServer.Instance._LockObjectWithVisibleRange(cr);
                                AutoLock al = new AutoLock(rl.handle);
                                cr.StopMove();
                                RappelzServer.Instance.SetMove(cr,returnPos,returnPos,0,false,0,true);
//                                ArcadiaAutoLock::_ArcadiaAutoLock((ArcadiaAutoLock *)(v1 - 96));
                            }
                        }
                    }
                }
            }
        }

        public ArPosition GetCurrentRoamingTargetPosition()
        {
            ArPosition pos = new ArPosition(this.m_vRoamingPoint[(int)this.m_nCurrentRoamingPointIndex]);
            return pos;

        }

        public ArPosition GetNextRoamingTargetPosition()
        {
            return new ArPosition(this.m_vRoamingPoint[(int)this.getNextRoamingTargetIndex()]);
        }

        public void PendHateShare(uint hRequester, uint hHateTarget, int nHate, int eApplyHateType)
        {
            if ((eApplyHateType & (int)this.m_eHateType) != 0)
            {
                lock(this)
                {
                    PendingHateShareInfo hateShare = new PendingHateShareInfo();
                    hateShare.m_hHateTarget = hHateTarget;
                    hateShare.m_hRequester = hRequester;
                    hateShare.m_nHate = nHate;

                    this.m_vPendingHateInfo.Add(hateShare);
                }
            }
        }

        protected bool isMovable()
        {

            foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
            {
                Creature pc = rci.m_pCreature;
                if (pc != null)
                {
                    if (pc.bIsInWorld && (pc.m_hEnemy != 0 || !pc.IsMovable()))
                        return false;
                }
            }
            return true;
        }

        protected void processWalk(uint t)
        {
// .text:00466664 t               = dword ptr  8
            ArMoveVector tmp_mv;                    // -112 -70h
// .text:00466664 _lock           = ArcadiaAutoLock ptr -30h
// .text:00466664 __lock          = ArcadiaLock ptr -18h
            uint rx;                                // -20  -14h
// 
            RLock rl = RappelzServer.Instance._LockObjectWithVisibleRange(this);
            AutoLock al = new AutoLock(rl.handle);
            tmp_mv = new ArMoveVector(this.mv);
//            ArcadiaAutoLock::_ArcadiaAutoLock((v2 - 48));

            tmp_mv.Step(t);

            if ((tmp_mv.x / GameContent.g_nRegionSize) != (this.mv.x / GameContent.g_nRegionSize) 
                || (tmp_mv.y / GameContent.g_nRegionSize) != (this.mv.y / GameContent.g_nRegionSize) 
                || !tmp_mv.bIsMoving)
            {
                rl = RappelzServer.Instance._LockArea((uint)(this.mv.x / GameContent.g_nRegionSize),
                    (uint)(this.mv.y / GameContent.g_nRegionSize),
                    (uint)(tmp_mv.x / GameContent.g_nRegionSize),
                    (uint)(tmp_mv.y / GameContent.g_nRegionSize));

//                 while ( 1 )
//                 {
//                     *(v2 - 16) = GameContent.g_nRegionSize;
//                     if ( v8 == (v3->baseclass_0.mv.baseclass_0.x / GameContent.g_nRegionSize) )
//                     {
//                         if ( *(v2 - 20) == (v3->baseclass_0.mv.baseclass_0.y / GameContent.g_nRegionSize) )
//                             break;
//                     }
//                     v14 = ArcadiaServer::Instance();
//                     ArcadiaServer::UnLock(v14, (v2 - 24));
//                     *(v2 - 16) = g_nRegionSize;
//                     v15 = *(v2 - 16);
//                     v8 = (v3->baseclass_0.mv.baseclass_0.x / GameContent.g_nRegionSize);
//                     v16 = *(v2 - 108);
//                     *(v2 - 20) = (v3->baseclass_0.mv.baseclass_0.y / GameContent.g_nRegionSize);
//                     v17 = (v16 / v15);
//                     v18 = *(v2 - 112) / v15;
//                     *(v2 - 16) = v17;
//                     v19 = *(v2 - 16);
//                     v20 = *(v2 - 20);
//                     v21 = ArcadiaServer::Instance();
//                     *(v2 - 24) = ArcadiaServer::_LockArea(
//                                      v21,
//                                      (v2 - 28),
//                                      v8,
//                                      v20,
//                                      v18,
//                                      v19,
//                                      "D:\\alienbrainWork\\Source Code\\@Rappelz\\Server\\GameServer_For_Update\\090721_GameServer\\Game\\Struct\\StructRoamer.cpp",
//                                      528)->handle;
//                 }
                al = new AutoLock(rl.handle);
                RappelzServer.Instance.onRegionChange(this, t - this.lastStepTime, !tmp_mv.bIsMoving);
//                ArcadiaAutoLock::_ArcadiaAutoLock((v2 - 48));
            }
        }

        protected void processRoaming(uint t, ref ArPosition currentPos, ref float fFace)
        {
            ArPosition pos;                         // -80  -50h
// .text:00466859 _lock           = ArcadiaAutoLock ptr -40h
            ArPosition posCurrent;                  // -44  -2Ch
            ArPosition posTarget;                   // -28  -1Ch
// .text:00466859 currentPos      = dword ptr  128  78h
// .text:00466859 fFace           = dword ptr  0Ch
            float fNewFace;                         // 56   38h
// .text:00466859 it              = std::_Vector_iterator<StructRoamer::ROAMING_CREATURE_INFO,std::allocator<StructRoamer::ROAMING_CREATURE_INFO> > ptr  60h
            Creature pCreature;                     // 104  68h
// .text:00466859 t               = dword ptr  116  74h

            uint nNeedToProcRespawnCount = 0;           // 120  78h
            bool bNeedToRegenFullHPMP;              // 127  7Fh

            RLock rl;
            AutoLock al;
            bool keepGoing = true;

            try
            {
                foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
                {
                    pCreature = rci.m_pCreature;
                    if (pCreature != null && pCreature.bIsInWorld)
                    {
                        rl = RappelzServer.Instance._LockObject(pCreature);
                        al = new AutoLock(rl.handle);
                        if (pCreature.mv.bIsMoving && pCreature.bIsInWorld || pCreature.m_hEnemy != 0)
                            return;
                        posTarget = Roamer.getCurrentRespawnObjectPosition(currentPos, ref fFace, ref rci.m_nAngle, ref rci.m_nDistance);
                        posCurrent = pCreature.GetCurrentPosition(t);
                        if (!posTarget.IsEqual(posCurrent))
                        {
                            RappelzServer.Instance.SetMove(pCreature, posCurrent, posTarget, (sbyte)this.m_nMoveSpeed, false, 0, true);
                            keepGoing = false;
                        }
//                        ArcadiaAutoLock::_ArcadiaAutoLock(v4);
                    }
                }
                if (!keepGoing)
                    return;
                if (this.m_eRoamingStatus == RoamingStatus.Roaming)
                {
                    this.mv.SetDirection(this.GetNextRoamingTargetPosition());
                    pos = this.GetCurrentRoamingTargetPosition();

                    fNewFace = this.mv.face;

                    foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
                    {
                        pCreature = rci.m_pCreature;
                        if (pCreature != null && pCreature.bIsInWorld)
                        {
                            rl = RappelzServer.Instance._LockObject(pCreature);
                            al = new AutoLock(rl.handle);

                            RappelzServer.Instance.SetMove(pCreature,pCreature.mv, Roamer.getCurrentRespawnObjectPosition(pos, ref fNewFace, ref rci.m_nAngle, ref rci.m_nDistance),
                                (sbyte)(this.m_nMoveSpeed * 1.5), false,0,true);
//                            ArcadiaAutoLock::_ArcadiaAutoLock((v4 - 64));
                        }
                    }
                    this.m_eRoamingStatus = RoamingStatus.Rotating;
                }
                else
                {
                    if (this.m_eRoamingStatus ==  RoamingStatus.Rotating)
                    {
                        this.proceedRoamingTargetIndex();
                    }
                    else
                    {
                        if (this.m_eRoamingStatus != RoamingStatus.Paused)
                            return;
                    }
                    pos = this.GetCurrentRoamingTargetPosition();

                    rl = RappelzServer.Instance._LockArea((uint)(this.mv.x / GameContent.g_nRegionSize),
                        (uint)(this.mv.y / GameContent.g_nRegionSize),
                        (uint)(pos.x / GameContent.g_nRegionSize),
                        (uint)(pos.y / GameContent.g_nRegionSize));
                    al = new AutoLock(rl.handle);

                    RappelzServer.Instance.SetMove(this, this.mv, pos, (sbyte)this.m_nMoveSpeed, false,0,false);
                    fFace = this.mv.face;
                    bNeedToRegenFullHPMP = true;
                    if ((this.m_AttributeFlag & 2) == 0 || this.m_nRespawnInterval == 0)
                        bNeedToRegenFullHPMP = false;

                    foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
                    {
                        pCreature = rci.m_pCreature;
                        if (pCreature != null && pCreature.bIsInWorld)
                        {
                            if (bNeedToRegenFullHPMP)
                            {
                                if (pCreature.m_nHP < pCreature.m_nMaxHP )
                                    pCreature.RegenFullHPMP();
                            }
                            pos = Roamer.getCurrentRespawnObjectPosition(pos, ref fFace,ref rci.m_nAngle, ref rci.m_nDistance);
                            RappelzServer.Instance.SetMove(pCreature, pCreature.mv,Roamer.getCurrentRespawnObjectPosition(pos, ref fFace,ref rci.m_nAngle, ref rci.m_nDistance),
                                (sbyte)this.m_nMoveSpeed,false,0,true);
                        }
                        else
                        {
                            ++nNeedToProcRespawnCount;
                        }
                    }
//                    ArcadiaAutoLock::_ArcadiaAutoLock((v4 - 100));
                    if (this.m_nRespawnInterval != 0)
                    {
                        if (nNeedToProcRespawnCount != 0)
                        {
                            if (this.m_nNextRespawnProcTime == 0)
                            {
                                if (!this.m_bIsRaidDungeonRoamer)
                                {
                                    uint nt = nNeedToProcRespawnCount == this.m_vRoamingCreatureRespawnInfo.Count ?  this.m_nRespawnInterval : 0;
                                    this.m_nNextRespawnProcTime = nt + Globals.GetArTime();
                                }
                            }
                        }
                    }
                    this.m_eRoamingStatus = RoamingStatus.Roaming;
                }

            }
            catch (System.Exception ex)
            {
            	
            }
            finally
            {
    //                        ArcadiaAutoLock::_ArcadiaAutoLock(v4);
            }
        }

        protected void processHateSharing()
        {
            List<PendingHateShareInfo> vHateProcBuffer = new List<PendingHateShareInfo>();
            Monster pMonster = null;

            lock(this)
            {
                for(int i = this.m_vPendingHateInfo.Count - 1; i >= 0; --i)
                {
                    PendingHateShareInfo hsi = this.m_vPendingHateInfo[i];
                    foreach (RoamingCreatureInfo rci in this.m_vRoamingCreatureRespawnInfo)
                    {
                        if (rci.m_pCreature != null
                            && rci.m_pCreature.m_hHandle != hsi.m_hRequester
                            && rci.m_pCreature.m_hHandle != hsi.m_hHateTarget
                            && rci.m_pCreature.IsMonster()
                            && rci.m_pCreature.bIsInWorld
                            && rci.m_pCreature.m_nHP != 0)
                        {
                            pMonster = rci.m_pCreature as Monster;
                            RLock rl = RappelzServer.Instance._LockObjectWithVisibleRange(pMonster);
                            AutoLock al = new AutoLock(rl.handle);

                            pMonster.AddHate(hsi.m_hHateTarget,hsi.m_nHate,true,false);
                            if (pMonster.GetStatus() == Monster.MonsterStatus.Normal)
                                pMonster.m_bNeedToFindEnemy = true;
//                                                    ArcadiaAutoLock::_ArcadiaAutoLock((ArcadiaAutoLock *)(v1 - 100));
                        }
                    }
                    this.m_vPendingHateInfo.RemoveAt(i);
                }
            }

        }

        protected uint getNextRoamingTargetIndex()
        {
            uint result = 0;

            int isize = this.m_vRoamingPoint.Count;
            if (isize > 1)
            {
                if (this.m_eCurrentRoamingDirection == Direction.Backward)
                {
                    if (this.m_nCurrentRoamingPointIndex != 0)
                    {
                        result = this.m_nCurrentRoamingPointIndex - 1;
                    }
                    else if (this.m_eRoamingType == RoamingType.GoBack)
                    {
                        result = 1;
                    }
                }
                else if (this.m_eCurrentRoamingDirection == Direction.Forward)
                {
                    if (this.m_nCurrentRoamingPointIndex < isize - 1)
                    {
                        result = this.m_nCurrentRoamingPointIndex + 1;
                    }
                    else if (this.m_eRoamingType == RoamingType.GoBack)
                    {
                        result = (uint)(isize - 2);
                    }
                }
            }
            return result;
        }

        protected void proceedRoamingTargetIndex()
        {
            uint result = 0;
            int isize = this.m_vRoamingPoint.Count;
            if (isize > 1)
            {
                if (this.m_eCurrentRoamingDirection != Direction.Forward)
                {
                    if (this.m_eCurrentRoamingDirection == Direction.Backward)
                    {
                        if (this.m_nCurrentRoamingPointIndex != 0)
                        {
                            result = this.m_nCurrentRoamingPointIndex - 1;
                        }
                        else
                        {
                            if (this.m_eRoamingType == RoamingType.GoBack)
                            {
                                this.m_eCurrentRoamingDirection = Direction.Forward;
                                result = 1;
                            }
                        }
                    }
                }
                else
                {
                    if (this.m_nCurrentRoamingPointIndex < isize - 1 )
                    {
                        result = this.m_nCurrentRoamingPointIndex + 1;
                    }
                    else
                    {
                        if (this.m_eRoamingType != RoamingType.Round)
                        {
                            if (this.m_eRoamingType == RoamingType.GoBack)
                            {
                                this.m_eCurrentRoamingDirection = Direction.Backward;
                                result = (uint)(isize - 2);
                            }
                        }
                    }
                }
                this.m_nCurrentRoamingPointIndex = result;
            }

        }

        protected static ArPosition getCurrentRespawnObjectPosition(ArPosition currentPos, ref float fFace, ref int nAngle, ref float nDistance)
        {
            double f1 = nAngle * 3.141592025756836 / 180.0 + fFace;
            float fs = (float)Math.Sin(f1);
            float fc = (float)Math.Cos(f1);

            ArPosition pos = new ArPosition(currentPos.x - nDistance * fc,currentPos.y - nDistance * fs,0);
            return pos;
        }


        public int m_nID;                                                       // 0xB8
        public int m_nMoveSpeed;                                                // 0xBC
        public RoamingType m_eRoamingType;                                      // 0xC0
        public HateType m_eHateType;                                            // 0xC4
        public uint m_nRespawnInterval;                                         // 0xC8
// Enum           :   _ATTRIBUTE_FLAG_IDX, Type: int
// Data           :     constant 0x1, Constant, Type: int, ATTRIBUTE_HEAL_HP_ON_COMEBACKHOME
        public int m_AttributeFlag;                                             // 0xCC
        public Direction m_eCurrentRoamingDirection;                            // 0xD0
        public uint m_nCurrentRoamingPointIndex;                                // 0xD4
        public RoamingStatus m_eRoamingStatus;                                  // 0xD8
        public bool m_bIsRaidDungeonRoamer;                                     // 0xDC
// Data           :   this+0xE0, Member, Type: class XCriticalSection, m_CS
        public List<ArPosition> m_vRoamingPoint = new List<ArPosition>();       // 0x120
        public List<RoamingCreatureInfo> m_vRoamingCreatureRespawnInfo = new List<RoamingCreatureInfo>();   // 0x130
        public uint m_nNextRespawnProcTime;                                     // 0x140
// Data           :   this+0x144, Member, Type: class XCriticalSection, m_csHate
        public List<PendingHateShareInfo> m_vPendingHateInfo = new List<PendingHateShareInfo>();            // 0x184
    }
}
